import time
import unittest
import copy
from datetime import datetime
import traceback
from UiManager.models import TestSuiteInfo, TestCaseInfo, ReportInfo, LogInfo, EnvInfo, SystemSettings
from UiManager.utils.UiUnittestTestCase import UiUnittestTestCase
from UiManager.utils.initialize import check_conf, startServer, jurisdiction, resetDevice, start_anyproxy, kill_anyproxy
from UiManager.utils.log import MyLogger
from UiManager.utils.report import get_summary, get_platform
from UiManager.utils.report import render_html_report


class UiRunner(object):
    def __init__(self, **kwargs):
        """ initialize test runner
        @param (dict) kwargs: key-value arguments used to initialize TextTestRunner
            - resultclass: HtmlTestResult or TextTestResult
            - failfast: False/True, stop the test run on the first error or failure.
            - dot_env_path: .env file path
        """
        self.runner = unittest.TextTestRunner(**kwargs)
        self.start_at = time.time()
        self.summary = {
            "success": True,
            "stat": {},
            "time": {
                'start_at': self.start_at
            },
            "platform": get_platform(),
            "details": []
        }

    def run(self, run_list):
        system_opt = SystemSettings.objects.get(id=1)
        # 转换为其他日期格式,如:"%Y-%m-%d %H:%M:%S"
        str_time = datetime.fromtimestamp(self.start_at).strftime('%Y-%m-%d %H:%M:%S')
        # 运行日志
        log_path = system_opt.log_dir + "/" + str_time + ".log"
        log = MyLogger(log_path)

        suite_id = run_list["suite"]
        suite_opt = TestSuiteInfo.objects
        suite_name = suite_opt.get(id=suite_id).suite_name

        # 保存log
        kwargs = {
            'log_name': str_time,
            'belong_suite': suite_opt.get(id=suite_id)
        }

        log_opt = LogInfo.objects
        log_opt.insert_log(**kwargs)

        log.info('开始自动化测试')
        log.info('运行环境信息：run_list为：%s' % str(run_list))
        base_url = run_list['env']['base_url']
        capabilities = run_list['env']['capabilities']
        capabilities = eval(capabilities)
        rule = run_list['env'].pop('rule', None)
        if rule:
            log.info('rule文件为：%s' % rule)

        def accumulate_stat(origin_stat, new_stat):
            """ accumulate new_stat to origin_stat
            """
            for key in new_stat:
                if key not in origin_stat:
                    origin_stat[key] = new_stat[key]
                elif key == "start_at":
                    origin_stat[key] = min(origin_stat[key], new_stat[key])
                else:
                    origin_stat[key] += new_stat[key]

        try:
            # 寻找设备
            check_info = check_conf(suite_name, 60)
            if check_info['status'] == '0':
                device_info = check_info['content']
                device_id = device_info['deviceid']
                log.info('查找到设备id：%s' % device_id)
            else:
                log.warning('没有找到可用设备')
                raise Exception('没有找到可用设备')

            # 获取设备id
            capabilities['deviceName'] = device_id

            # 处理权限
            log.info('处理app权限，appPath:%s' % capabilities['app'])
            message = jurisdiction(deviceid=device_id, package=capabilities['appPackage'], app_path=capabilities['app'])
            if message != 'success':
                log.error(message)
                raise Exception('权限处理失败')

            # 启动服务
            log.info('启动appium服务，device_info:%s' % str(device_info))
            startServer(device_info=device_info)

            # 启动anyproxy服务
            start_anyproxy(rule=rule, device_info=device_info)

            # 从套件中获取case列表
            cases = suite_opt.get(id=suite_id).cases.split(',')
            log.info('开始运行套件，套件名称:%s' % suite_name)
            # 执行case
            for case in cases:
                case_start_at = time.time()
                case_name = TestCaseInfo.objects.get(id=case).case_name
                log.info('开始运行测试用例，case 名称:%s' % case_name)
                test_step_action = unittest.TestSuite()
                class_name = UiUnittestTestCase
                func = "test_run_method"
                log.info("capabilities的内容:%s" % str(capabilities))
                test_step_action.addTest(class_name(func, case=case, base_url=base_url,
                                                    port=device_info['port'], capabilities=capabilities, log=log))
                try:
                    step_run_result = self.runner.run(test_step_action)
                    # 记录执行结果
                    if 0 != len(step_run_result.errors):
                        error = step_run_result.errors
                        log.error('测试用例：%s 测试未通过,失败原因：%s' % (case_name, str(error)))
                    elif 0 != len(step_run_result.failures):
                        error = step_run_result.failures
                        log.error('测试用例：%s 测试未通过,失败原因：%s'% (case_name, str(error)))
                    else:
                        log.error('测试用例：%s 测试通过' % case_name)
                        error = 'NULL'
                except Exception as e:
                    log.warning('执行出错，问题描述：%s' % traceback.print_exc())

                test_case_summary = get_summary(step_run_result, case_start_at)
                self.summary["success"] &= test_case_summary["success"]
                test_case_summary["name"] = case_name
                test_case_summary["base_url"] = base_url
                test_case_summary["output"] = copy.copy(UiUnittestTestCase.outputs_dict)
                test_case_summary["error"] = copy.copy(error)
                accumulate_stat(self.summary["stat"], test_case_summary["stat"])
                self.summary["details"].append(test_case_summary)
            log.info('结束测试')
            log.info('杀掉anyrpxoy，端口号%s' % device_info['aport'])
            kill_anyproxy(device_info['aport'])
            # 重置设备和套件状态
            log.info('重置设备与套件状态，device_id:%s, suite_id:%s' % (device_id, suite_id))
            resetDevice(deviceid=device_id, suite_id=suite_id)
        except Exception as e:
            log.error(e)
        finally:
            # 重置设备和套件状态
            if 'device_id' not in vars():
                device_id = None
            if 'suite_id' not in vars():
                suite_id = None
            log.info('重置设备与套件状态，device_id:%s, suite_id:%s' % (device_id, suite_id))
            duration = int(time.time()) - int(self.summary["time"]['start_at'])
            resetDevice(deviceid=device_id, suite_id=suite_id, duration=duration)
            self.summary["time"]['duration'] = duration

    def gen_html_report(self, html_report_name=None, html_report_template=None):
        """ generate html report and return report path
        @param (str) html_report_name:
            output html report file name
        @param (str) html_report_template:
            report template file path, template should be in Jinja2 format
        """
        return render_html_report(
            self.summary,
            html_report_name,
            html_report_template
        )
